#include "kvector.h"

#include "sysapi.h"

#include <memory.h>


#define DEFAULT_CAPACITY 2

typedef unsigned char uint8_t;

typedef struct KstlVector {
    size_t size;
    size_t capacity;
    size_t itemSize;
    void*  storage;
} KstlVector;

static int Relocate(KstlVector* handle)
{
    size_t newCapacity = handle->capacity * 2;
    void*  newStorage  = KstlAllocate(handle->itemSize * newCapacity);
    if (!newStorage) {
        return -1;
    }

    memcpy(newStorage, handle->storage, handle->size * handle->itemSize);

    KstlRelease(handle->storage);
    handle->storage = newStorage;

    handle->capacity = newCapacity;

    return 0;
}

KstlVector* KstlVectorInit(size_t itemSize)
{
    return KstlVectorInitWithCapacity(itemSize, DEFAULT_CAPACITY);
}

KstlVector* KstlVectorInitWithCapacity(size_t itemSize, size_t capacity)
{
    return KstlVectorInitWithCapacityAndValue(itemSize, capacity, NULL);
}

KstlVector* KstlVectorInitWithCapacityAndValue(size_t itemSize, size_t capacity, void* defaultValue)
{
    KstlVector* handle = KstlAllocate(sizeof(KstlVector));
    if (!handle) {
        return NULL;
    }

    memset(handle, 0, sizeof(KstlVector));

    handle->itemSize = itemSize;
    handle->capacity = capacity;

    handle->storage = KstlAllocate(itemSize * handle->capacity);
    if (!handle->storage) {
        KstlRelease(handle);
        return NULL;
    }

    memset(handle->storage, 0, itemSize * handle->capacity);

    if (defaultValue) {
        for (size_t i = 0; i < capacity; i++) {
            KstlVectorPushBack(handle, defaultValue);
        }
    }

    return handle;
}

void KstlVectorRelease(KstlVector* handle)
{
    if (!handle) {
        return;
    }

    if (handle->storage) {
        KstlRelease(handle->storage);
    }

    KstlRelease(handle);
    handle = NULL;
}

size_t KstlVectorSize(KstlVector* handle)
{
    return handle->size;
}

size_t KstlVectorCapacity(KstlVector* handle)
{
    return handle->capacity;
}

int KstlVectorPushBack(KstlVector* handle, void* item)
{
    if (!handle) {
        return -1;
    }

    if (handle->size >= handle->capacity) {
        Relocate(handle);
    }

    void* newItemStart = (uint8_t*)handle->storage + handle->size * handle->itemSize;
    memcpy(newItemStart, item, handle->itemSize);

    handle->size++;

    return 0;
}

int KstlVectorPopBack(KstlVector* handle)
{
    if (handle->size == 0) {
        return -1;
    }

    handle->size--;

    return 0;
}

const void* KstlVectorData(KstlVector* handle)
{
    return handle->storage;
}

void* KstlVectorReadAt(KstlVector* handle, size_t i)
{
    if (!handle->storage) {
        return NULL;
    }

    if (i > handle->size) {
        return NULL;
    }

    return (uint8_t*)handle->storage + handle->itemSize * i;
}

int KstlVectorWriteAt(KstlVector* handle, size_t i, void* item)
{
    if (!handle->storage) {
        return -1;
    }

    if (i > handle->size) {
        return -1;
    }

    void* itemStart = (uint8_t*)handle->storage + i * handle->itemSize;
    memcpy(itemStart, item, handle->itemSize);
    return 0;
}

KstlVectorIterator KstlVectorIteratorBegin(KstlVector* handle)
{
    KstlVectorIterator it = {.handle = handle, .pointer = handle->storage};
    return it;
}

KstlVectorIterator KstlVectorIteratorEnd(KstlVector* handle)
{
    KstlVectorIterator it;
    it.handle  = handle;
    it.pointer = (char*)handle->storage + handle->size * handle->itemSize;
    return it;
}

KstlVectorIterator KstlVectorIteratorFromIndex(KstlVector* handle, size_t index)
{
    KstlVectorIterator it;
    void*              border = (char*)handle->storage + handle->size * handle->itemSize;

    void* itPosition = (char*)handle->storage + index * handle->itemSize;
    if (itPosition >= border) {
        return KstlVectorIteratorEnd(handle);
    }

    it.handle  = handle;
    it.pointer = itPosition;
    return it;
}

void KstlVectorIteratorNext(KstlVectorIterator* it)
{
    it->pointer = (char*)it->pointer + it->handle->itemSize;
}

KstlVectorIterator KstlVectorIteratorReverseBegin(KstlVector* handle)
{
    KstlVectorIterator it;
    it.handle  = handle;
    it.pointer = (char*)handle->storage + ((handle->size - 1) * handle->itemSize);
    return it;
}

KstlVectorIterator KstlVectorIteratorReverseEnd(KstlVector* handle)
{
    KstlVectorIterator it;
    it.handle  = handle;
    it.pointer = (char*)handle->storage - handle->itemSize;
    return it;
}

void KstlVectorIteratorReverseNext(KstlVectorIterator* it)
{
    it->pointer = (char*)it->pointer - it->handle->itemSize;
}

bool KstlVectorIteratorEqual(KstlVectorIterator current, KstlVectorIterator end)
{
    return current.pointer == end.pointer;
}

void* KstlVectorIteratorValue(KstlVectorIterator current)
{
    return current.pointer;
}

void KstlVectorIteratorPrev(KstlVectorIterator* it)
{
    it->pointer = (char*)it->pointer - it->handle->itemSize;
}